/*
该模块对日志信息进行格式化输出
*/

#ifndef __M_FORMATTER_H__
#define __M_FORMATTER_H__

#include "Message.hpp"
#include <memory> // 智能指针
#include <vector>
#include <sstream>
#include <cassert>

namespace LogModule
{
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual void format(std::ostream &os, const LogMessage &message) = 0;
    };
    /*
        %d -- 日期
        %t -- 线程ID
        %p -- 日志级别
        %c -- 日志器名称
        %f -- 源文件名称
        %l -- 行号
        %m -- 日志消息
        %n -- 换行
        %T -- 缩进
        [ ] { } -- 其它信息
    */
    // 时间 -- %d -- 需要传入格式，%H:%M:%S，通过格式判断输出什么格式的时间内容
    class TimeFormatItem : public FormatItem
    {
    public:
        TimeFormatItem(std::string format = "%H:%M:%S") : _time_format(format) {}
        void format(std::ostream &os, const LogMessage &message)
        {
            struct tm time;
            // 将时间戳转换为本地时序日历，结果写入time结构体中
            localtime_r(&message._ctime, &time);
            char tmp[32] = {0};
            // 把时间按照指定格式输出，写入到tmp数组中
            strftime(tmp, sizeof(tmp), _time_format.c_str(), &time);
            // 1. 直接将格式化的时间写入到对应的流中(文件、标准输出等都可以)
            os << tmp;
        }

    private:
        std::string _time_format; // 需要知道以什么格式输出时间
    };
    // 线程ID -- %t
    class ThreadFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const LogMessage &message)
        {
            os << message._tid;
        }
    };
    // 日志级别 -- %p
    class LevelFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const LogMessage &message)
        {
            os << LogLevel::ToString(message._level);
        }
    };
    // 日志器名称 -- %c
    class NameFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const LogMessage &message)
        {
            os << message._name;
        }
    };
    // 源文件名称 -- %f
    class FileFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const LogMessage &message)
        {
            os << message._file;
        }
    };
    // 行号 -- %l
    class LineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const LogMessage &message)
        {
            os << message._line;
        }
    };
    // 日志消息 -- %m
    class MessageFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const LogMessage &message)
        {
            os << message._payload;
        }
    };
    // 换行 -- %n
    class NewLineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const LogMessage &message)
        {
            os << "\n";
        }
    };
    // 缩进 -- %T
    class TableFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &os, const LogMessage &message)
        {
            os << "\t";
        }
    };
    // 其它信息 -- [] {} -- 需要手动传入这些信息
    class OtherFormatItem : public FormatItem
    {
    public:
        OtherFormatItem(std::string str) : _str(str) {}
        void format(std::ostream &os, const LogMessage &message)
        {
            os << _str;
        }

    private:
        std::string _str;
    };

    // 日志格式化类的实现
    // 日志格式化类的作用为：解析日志格式，并按照格式输出日志内容
    class Formatter
    {
    public:
        using ptr = std::shared_ptr<Formatter>;
        // 需要知道日志按照什么格式输出，有默认格式
        Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n")
            : _pattern(pattern)
        {
            assert(ParsePattern()); // 只能传入可以解析的日志格式
        }
        // 对传入的数据进行格式化，写入到os中
        void Format(std::ostream &os, const LogMessage &message)
        {
            // 有了items数组，直接进行写入操作即可
            for (auto &item : _items)
            {
                item->format(os, message);
            }
        }
        std::string Format(const LogMessage &message)
        {
            // 这不过就是向ss流中写入
            // 可以向stringstream写入任意类型，使用str接口可以将ss转为字符串
            std::stringstream ss;
            Format(ss, message);
            return ss.str();
        }

    private:
        // 日志格式的解析工作
        bool ParsePattern()
        {
            // 1. 注意1：%sab%%cd[%d{%H:%M:%S}] -- 一开始就是%，特殊情况处理
            // 2. 每次pos++之后，都要判断pos是否越界
            // 实现1. 对abcd[%d{%H:%M:%S}]进行格式解析
            // 多组key(d) value(%H:%M:%S)，按照顺序执行
            std::vector<std::pair<std::string, std::string>> fmt_order;
            size_t pos = 0;
            std::string key = "", value = "";
            while (pos < _pattern.size())
            {
                if (_pattern[pos] != '%')
                {
                    // 如果没有碰到%，只能说明碰到的是普通数据abcd[]
                    value.push_back(_pattern[pos++]);
                    continue;
                }
                // 走到这里，说明碰到了%
                if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%')
                {
                    // 如果遇到%%，说明想要传入的就是%
                    value.push_back('%');
                    pos += 2;
                    continue;
                }
                if (!value.empty())
                {
                    // nullptr  abcd[
                    fmt_order.push_back(std::make_pair("", value));
                    value.clear(); // 注意：需要清空value
                }
                if (pos + 1 >= _pattern.size())
                {
                    std::cout << "%后面没有跟任何东西，无法解析！\n";
                    return false;
                }
                // 走到这里，说明%后面跟的就是格式了！
                pos++;
                key.push_back(_pattern[pos++]); // 取出格式d
                if (pos < _pattern.size() && _pattern[pos] == '{')
                {
                    // 如果有{，说明有格式，比如日期格式%H:%M:%S
                    while (pos < _pattern.size() && _pattern[pos] != '}')
                    {
                        value.push_back(_pattern[pos++]);
                    }
                    if (pos >= _pattern.size())
                    {
                        std::cout << "{没有与之匹配的}，{格式错误，无法解析\n";
                        return false;
                    }
                    // 说明拿到了d  %H:%M:%S
                }
                // 走到这里，说明没有{
                fmt_order.push_back(std::make_pair(key, value));
                key.clear();
                value.clear();
                continue;
            }
            // 此时value可能不为空，测试2：ab%%cd[%d{%H:%M:%S}]，输出：ab%cd[{20:48:49
            if (!value.empty())
                fmt_order.push_back(std::make_pair(key, value));
            // 实现2. 然后我们就得到了一系列的处理结果，全部被保存在fmt_order中
            for (auto &it : fmt_order)
            {
                // 然后就可以得到items了！
                _items.push_back(CreateItem(it.first, it.second));
            }
            return true;
        }

    private:
        // 根据不同的格式化字符创建不同的格式化子项对象
        FormatItem::ptr CreateItem(const std::string &key, const std::string &val)
        {
            if (key == "d")
                return FormatItem::ptr(new TimeFormatItem(val));
            if (key == "t")
                return FormatItem::ptr(new ThreadFormatItem());
            if (key == "p")
                return FormatItem::ptr(new LevelFormatItem());
            if (key == "c")
                return FormatItem::ptr(new NameFormatItem());
            if (key == "f")
                return FormatItem::ptr(new FileFormatItem());
            if (key == "l")
                return FormatItem::ptr(new LineFormatItem());
            if (key == "m")
                return FormatItem::ptr(new MessageFormatItem());
            if (key == "n")
                return FormatItem::ptr(new NewLineFormatItem());
            if (key == "t")
                return FormatItem::ptr(new TableFormatItem());
            if (key.empty())
                return FormatItem::ptr(new OtherFormatItem(val));
            std::cout << "没有对应的格式化字符：%" << key << "我们什么都没做" << std::endl;
            return FormatItem::ptr(new OtherFormatItem(val));
        }

    private:
        std::string _pattern; // 待解析的格式字符串[%d{%H:%M:%S}]
        std::vector<FormatItem::ptr> _items;
    };
}

#endif